package com.es.demo.controller;

import com.alibaba.fastjson.JSONObject;
import com.es.demo.pojo.Pageable;
import com.es.demo.pojo.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.metrics.*;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author: zqf
 * @Date: 2022/10/13/23:03
 * @Description:
 */

@RestController("/query")
@Api(tags = "查询文档")
public class QueryDocController {

    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Value("${elasticsearch.index}")
    private String esIndex;


    @GetMapping("/QueryBuilders-matchAllQuery")
    @ApiOperation("QueryBuilders.matchAllQuery()查询所有")
    private List<User> allSearch() throws IOException {
        //构建查询
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //QueryBuilders.matchAllQuery()查询全部
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        searchRequestAll.source(searchSourceBuilder);
        //同步查询
        SearchResponse searchResponseAll = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        //获取总条数
        TotalHits totalHits = searchResponseAll.getHits().getTotalHits();
        //获取数据
        SearchHits hits = searchResponseAll.getHits();
        SearchHit[] hits1 = hits.getHits();
        //转为stream
        Stream<SearchHit> stream = Arrays.stream(hits1);
        //转成json字符串
        List<String> list = stream.map(getGetSourceAsString()).collect(Collectors.toList());
        //集合封装返回
        List<User> users = new ArrayList<>();
        for (String item : list) {
            User user = JSONObject.parseObject(item, User.class);
            users.add(user);
        }
        //stream封装方式
        List<User> userList = Arrays.stream(hits1)
                .map(getGetSourceAsString())
                .map(item -> JSONObject.parseObject(item, User.class))
                .collect(Collectors.toList());

        System.out.print("total = " + totalHits);
        return users;
    }


    /**
     * 返回与提供的文本、数字、日期或布尔值匹配的文档。在匹配之前分析提供的文本。
     * 匹配查询是执行全文搜索的标准查询，包括模糊匹配选项。
     */
    @GetMapping("/QueryBuilders-matchQuery")
    @ApiOperation("QueryBuilders.matchQuery()会根据分词器进行分词，分词之后去查询")
    private List<User> matchQuery(@RequestParam("name") @ApiParam("用户名") String name) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //QueryBuilders.matchQuery()分词结果进行模糊查询，只要文档数据中有一个词项命中，就返回结果
        MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("name", name);

        //返回此查询中使用的字段名
        //matchQuery.fieldName();

        //设置模糊匹配查询的公共（非模糊）前缀长度 默认是0
        //matchQuery.prefixLength();

        //获取计算为模糊查询类型时使用的模糊度 默认是Fuzziness.AUTO
        //matchQuery.fuzziness(Fuzziness.AUTO);

        searchSourceBuilder.query(matchQuery);

        searchRequestAll.source(searchSourceBuilder);

        SearchResponse search = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();

        return Arrays.stream(hits)
                .map(getGetSourceAsString())
                .map(str -> JSONObject.parseObject(str, User.class))
                .collect(Collectors.toList());
    }


    @GetMapping("/matchQuery-operator")
    @ApiOperation("matchQuery.operator()指定分词关系")
    public List<User> matchQueryAnd(@RequestParam("name") @ApiParam("name") String name,
                                    @RequestParam("operator") @ApiParam("1代表的是Operator.AND 2代表的Operator.OR") Integer operator) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //QueryBuilders.matchQuery()分词结果进行模糊查询，只要文档数据中有一个词项命中，就返回结果
        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("name", name);

        //Operator.AND、OR相当于 sql中的and和or，这里的用处是分词后的关系 如果是and则需要满足分词后的所有词条，如果是OR则满足其中一个词条都可以。
        if (operator == 1) {
            queryBuilder.operator(Operator.AND);
        }
        if (operator == 2) {
            queryBuilder.operator(Operator.OR);
        }

        searchSourceBuilder.query(queryBuilder);

        searchRequestAll.source(searchSourceBuilder);

        SearchResponse search = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();

        return Arrays.stream(hits)
                .map(getGetSourceAsString())
                .map(str -> JSONObject.parseObject(str, User.class))
                .collect(Collectors.toList());
    }

    @GetMapping("/matchQuery-analyzer")
    @ApiOperation("matchQuery.analyzer() 指定分词器")
    public String analyzer() {
        return "Standard Analyzer：默认分词器，按词切分，小写处理\n" +
                "Simple Analyzer：按照非字母切分（符号被过滤），小写处理\n" +
                "Stop Analyzer：停用词过滤（the，a，is），小写处理\n" +
                "Whitespace Analyzer：按照空格切分，不转小写\n" +
                "Keyword Analyzer：直接将输入当做输出，不分词\n" +
                "Patter Analyzer：正则表达式，默认 \\W+（非字符分割）\n" +
                "Language：提供了30多种常见语言的分词器";
    }


    @GetMapping("/QueryBuilders-wildcardQuery")
    @ApiOperation("QueryBuilders.wildcardQuery()模糊查询")
    private List<User> wildcardQuery(@RequestParam("name") @ApiParam("用户名") String name) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //QueryBuilders.matchQuery()模糊查询指定条件相关数据
        searchSourceBuilder.query(QueryBuilders.wildcardQuery("name", "*" + name + "*"));
        searchRequestAll.source(searchSourceBuilder);

        SearchResponse search = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();

        return Arrays.stream(hits)
                .map(getGetSourceAsString())
                .map(str -> JSONObject.parseObject(str, User.class))
                .collect(Collectors.toList());
    }


    @GetMapping("/QueryBuilders-termQuery")
    @ApiOperation("QueryBuilders.termQuery()不会进行分词，且完全等于才会匹配")
    private List<User> termQuery(@RequestParam("name") @ApiParam("用户名") String name) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        TermQueryBuilder termQuery = QueryBuilders.termQuery("name", name);
        searchSourceBuilder.query(termQuery);
        searchRequestAll.source(searchSourceBuilder);

        SearchResponse search = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();

        return Arrays.stream(hits)
                .map(getGetSourceAsString())
                .map(str -> JSONObject.parseObject(str, User.class))
                .collect(Collectors.toList());
    }


    @GetMapping("/QueryBuilders-termsQuery")
    @ApiOperation("QueryBuilders.termsQuery()一个字段匹配多个值，where name = ‘name1’ or name = ‘name2’")
    private List<User> termsQuery(@RequestParam("name1") @ApiParam("用户名1") String name1,
                                  @RequestParam("name2") @ApiParam("用户名2") String name2) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        TermsQueryBuilder termQuery = QueryBuilders.termsQuery("name", name1, name2);
        searchSourceBuilder.query(termQuery);
        searchRequestAll.source(searchSourceBuilder);

        SearchResponse search = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();

        return Arrays.stream(hits)
                .map(getGetSourceAsString())
                .map(str -> JSONObject.parseObject(str, User.class))
                .collect(Collectors.toList());
    }


    @GetMapping("/QueryBuilders-matchPhraseQuery")
    @ApiOperation("QueryBuilders.matchPhraseQuery()不会分词，当成一个整体去匹配，相当于 %like%")
    private List<User> matchPhraseQuery(@RequestParam("name") @ApiParam("用户名") String name) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        MatchPhraseQueryBuilder matchPhraseQuery = QueryBuilders.matchPhraseQuery("name", name);
        searchSourceBuilder.query(matchPhraseQuery);
        searchRequestAll.source(searchSourceBuilder);

        SearchResponse search = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();

        return Arrays.stream(hits)
                .map(getGetSourceAsString())
                .map(str -> JSONObject.parseObject(str, User.class))
                .collect(Collectors.toList());
    }

    @GetMapping("/QueryBuilders-rangeQuery")
    @ApiOperation("QueryBuilders.rangeQuery()范围查询+排序")
    private List<User> matchPhraseQuery(@RequestParam("startAge") @ApiParam("开始年龄") Integer startAge,
                                        @RequestParam("endAge") @ApiParam("结束年龄") Integer endAge) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").gt(startAge).lt(endAge);
        searchSourceBuilder.query(rangeQueryBuilder);

        searchSourceBuilder.sort("age", SortOrder.DESC);
        searchRequestAll.source(searchSourceBuilder);


        SearchResponse search = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();

        return Arrays.stream(hits)
                .map(getGetSourceAsString())
                .map(str -> JSONObject.parseObject(str, User.class))
                .collect(Collectors.toList());
    }


    @GetMapping("/QueryBuilders-boolQuery")
    @ApiOperation("QueryBuilders.boolQuery()组合查询 must(必须满足)、must not(必须不满足)、should(最好是可以满足)")
    private List<User> boolQuery(@RequestParam("age") @ApiParam("年龄(必须大于)") Integer age,
                                 @RequestParam("name") @ApiParam("名字(必须满足)") String name) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //查询名字必须包含name和age必须大于输入年纪的.并且名字不能是student-4，最好address是包含油榨街的
        boolQueryBuilder.must(QueryBuilders.wildcardQuery("name", "*" + name + "*"));
        boolQueryBuilder.must(QueryBuilders.rangeQuery("age").gt(age));
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("name", "student-4"));
        boolQueryBuilder.should(QueryBuilders.wildcardQuery("address", "*南明区*"));

        searchSourceBuilder.query(boolQueryBuilder);

        searchSourceBuilder.sort("age", SortOrder.DESC);
        searchRequestAll.source(searchSourceBuilder);

        SearchResponse search = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();

        return Arrays.stream(hits)
                .map(getGetSourceAsString())
                .map(str -> JSONObject.parseObject(str, User.class))
                .collect(Collectors.toList());
    }


    @PostMapping("/queryPage")
    @ApiOperation("分页查询")
    private List<User> boolQuery(@RequestBody Pageable pageable) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        MatchAllQueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();
        searchSourceBuilder.sort("age", SortOrder.ASC);
        searchSourceBuilder.from((pageable.getPageNum() - 1) * pageable.getPageSize()).size(pageable.getPageSize());
        searchSourceBuilder.query(matchAllQuery);
        searchRequestAll.source(searchSourceBuilder);

        SearchResponse search = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();

        return Arrays.stream(hits)
                .map(getGetSourceAsString())
                .map(str -> JSONObject.parseObject(str, User.class))
                .collect(Collectors.toList());
    }

    @GetMapping("/aggregationQuery")
    @ApiOperation("聚合查询 max min avg count sum cardinality")
    private Object aggregationQuery(@RequestParam("aggregation") @ApiParam("输入要执行的操作max min avg count sum cardinality") String aggregation) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        SearchResponse searchResponse;
        switch (aggregation) {
            case "max":
                MaxAggregationBuilder aggregationBuilder = AggregationBuilders.max(aggregation).field("age");
                searchSourceBuilder.aggregation(aggregationBuilder);
                searchRequestAll.source(searchSourceBuilder);
                searchResponse = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
                Max max = searchResponse.getAggregations().get(aggregation);
                return max.getValue();
            case "min":
                MinAggregationBuilder minAggregationBuilder = AggregationBuilders.min(aggregation).field("age");
                searchSourceBuilder.aggregation(minAggregationBuilder);
                searchRequestAll.source(searchSourceBuilder);
                searchResponse = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
                Min min = searchResponse.getAggregations().get(aggregation);
                return min.getValue();
            case "avg":
                AvgAggregationBuilder avgAggregationBuilder = AggregationBuilders.avg(aggregation).field("age");
                searchSourceBuilder.aggregation(avgAggregationBuilder);
                searchRequestAll.source(searchSourceBuilder);
                searchResponse = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
                Avg avg = searchResponse.getAggregations().get(aggregation);
                return avg.getValue();
            case "count":
                ValueCountAggregationBuilder countAggregationBuilder = AggregationBuilders.count(aggregation).field("age");
                searchSourceBuilder.aggregation(countAggregationBuilder);
                searchRequestAll.source(searchSourceBuilder);
                searchResponse = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
                ValueCount count = searchResponse.getAggregations().get(aggregation);
                return count.getValue();
            case "sum":
                SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum(aggregation).field("age");
                searchSourceBuilder.aggregation(sumAggregationBuilder);
                searchRequestAll.source(searchSourceBuilder);
                searchResponse = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
                Sum sum = searchResponse.getAggregations().get(aggregation);
                return sum.getValue();
            case "cardinality":
                CardinalityAggregationBuilder cardinalityAggregationBuilder = AggregationBuilders.cardinality(aggregation).field("age");
                searchSourceBuilder.aggregation(cardinalityAggregationBuilder);
                searchRequestAll.source(searchSourceBuilder);
                searchResponse = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
                Cardinality cardinality = searchResponse.getAggregations().get(aggregation);
                return cardinality.getValue();
            default:
                throw new IllegalStateException("Unexpected value: " + aggregation);
        }
    }


    @GetMapping("/groupQuery")
    @ApiOperation("分组查询")
    private Object groupQuery(@RequestParam("aggregation") @ApiParam("分组的字段") String filed) throws IOException {
        SearchRequest searchRequestAll = new SearchRequest(esIndex);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(AggregationBuilders.terms(filed + "-group").field(filed));
        searchRequestAll.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequestAll, RequestOptions.DEFAULT);
        Aggregations aggregations = searchResponse.getAggregations();
        Map<String, Aggregation> asMap = aggregations.getAsMap();
        Aggregation aggregation = asMap.get(filed + "-group");
        String type = aggregation.getType();
//        StringTerms groupByCountry = (StringTerms);
//        List<StringTerms.Bucket> buckets = groupByCountry.getBuckets();
//        for (StringTerms.Bucket bucket : buckets) {
//            System.out.println("bucketKey = " + bucket.getKey() + "bucketDoc = " + bucket.getDocCount());
//        }
        return null;
    }

    private Function<SearchHit, String> getGetSourceAsString() {
        return SearchHit::getSourceAsString;
    }


    @GetMapping("/search-id")
    @ApiOperation("查询指定id")
    public User getDocumentById(@RequestParam("id") @ApiParam("id") String id) throws IOException {
        GetRequest request = new GetRequest(esIndex, id);
        GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);

        //转成string
        String sourceAsString = response.getSourceAsString();

        if (sourceAsString == null) {
            throw new IOException("找不到对应数据");
        }

        //转成map
        Map<String, Object> sourceAsMap = response.getSourceAsMap();
        return JSONObject.parseObject(sourceAsString, User.class);
    }

}
